﻿using System;
using System.IO;
using System.Net;
using System.Text;
using my_proxy.Utils;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;


namespace my_proxy.RequestHandlers
{
    /// <summary>
    ///  Handle one request of the client from the server.
    /// </summary>
    public class OpenRequestHandler
    {
        protected HttpListenerContext Context;
        protected FileReader FileReader;
        protected object FileLock;
        protected Configuration Conf;
 

        /// <summary>
        /// Initialize the communication with the Remote Server - Open mode
        /// </summary>
        /// <param name="cf">The configuration file</param>
        /// <param name="context">The context of the request</param>
        /// <param name="fr">The file reader object</param>
        /// <param name="fileLock">A lock for synchonization</param>
        public OpenRequestHandler(Configuration cf,
            HttpListenerContext context, FileReader fr,object fileLock)
        {
            this.Context = context;
            this.RemoteUrl = this.Context.Request.RawUrl;
            this.FileReader = fr;
            this.FileLock = fileLock;
            this.Conf = cf;
            if (context.Request.RemoteEndPoint != null)
            {
                Log.Info(context.Request.RemoteEndPoint.Address + " " + context.Request.RawUrl);
               // Log.Info("Email blocked: user@g.com");
            }
            else
            {
                string contextErrMsg = "Context error in: " + context.Request.RawUrl;
                Log.Info(contextErrMsg);
                throw new Exception(contextErrMsg);
            }
        }

        /// <summary>
        /// Main method - Method calls when client request the server
        /// </summary>
        public void ProcessRequest()
        {
            // Create a request with same data in navigator request
   
            HttpWebRequest request = this.GetRequest();

            try
            {
                // Send the request to the remote server and return the response
                HttpWebResponse response = this.GetResponse(request);
                if (response != null)
                {   // if the server return a response - manipulate it (add cookies, etc.) 
                    // and send it back to the client
                    byte[] responseData = this.GetResponseStreamBytes(response);
                    // transfer cookies to the client from the server...

                    // send the response back to the client
                    this.SendResponse(response, responseData);
                }
            }
            catch (Exception)
            {
                Console.WriteLine("Did not complete handling request");
            }
        }


        /// <summary>
        /// Return address to communicate to the remote server
        /// </summary>
        public string RemoteUrl { get; private set; }



        public void CheckIfChange( string before ,string after , string type ,string change){
            if (!before.Equals(after))
            {
                for (int i = before.ToLower().IndexOf(type); i > -1; i = before.ToLower().IndexOf(type, i + 1))
              {
                  // for loop end when i=-1 ('a' not found)
                  if (type.Length == 3)
                  {
                      Log.Info("Word transformed: " + before.Substring(i, 3) + "->" + change);
                  }
                  else
                  {
                      Log.Info("Word transformed: " + before.Substring(i, 2) + "->" + change);
                  }
                    
              }
               
            }
            
         }

        /// <summary>
        /// Create a request the remote server
        /// </summary>
        /// <returns>Request to send to the server </returns>
        protected virtual HttpWebRequest GetRequest()
        {
            int index = this.Context.Request.RawUrl.IndexOf("?");
            if (index != -1)
            {
                string start = this.Context.Request.RawUrl.Substring(0, index);
                
                string input = HttpUtility.UrlDecode(this.Context.Request.RawUrl.Substring(index));
                input = System.Uri.UnescapeDataString(input);
                string pattern = @"\bbgu\b";
                string replace = "BenGurion";

                Regex emailRegex = new Regex(@"\bbgu\b", RegexOptions.IgnoreCase);
                //find items that matches with our pattern
                MatchCollection emailMatches = emailRegex.Matches(input.ToString());
                foreach (Match emailMatch in emailMatches)
                {
                    Log.Info("Word transformed: " + emailMatch.Value + " -> BenGurion");
                }

                string res = Regex.Replace(input, pattern, replace, RegexOptions.IgnoreCase);
                

                pattern = @"\bise\b";
                replace = "InformationSystemEngineering";

                emailRegex = new Regex(@"\bise\b", RegexOptions.IgnoreCase);
                //find items that matches with our pattern
                emailMatches = emailRegex.Matches(res.ToString());
                foreach (Match emailMatch in emailMatches)
                {
                    Log.Info("Word transformed: " + emailMatch.Value + " -> InformationSystemEngineering");
                }

                res = Regex.Replace(res, pattern, replace, RegexOptions.IgnoreCase);

                pattern = @"\bse\b";
                replace = "SoftwareEngineering";
                emailRegex = new Regex(@"\bse\b", RegexOptions.IgnoreCase);
                //find items that matches with our pattern
                emailMatches = emailRegex.Matches(res.ToString());
                foreach (Match emailMatch in emailMatches)
                {
                    Log.Info("Word transformed: " + emailMatch.Value + " -> SoftwareEngineering");
                }
                res = Regex.Replace(res, pattern, replace, RegexOptions.IgnoreCase);

                this.RemoteUrl = start + res;

            }

            HttpWebRequest requestToServer = (HttpWebRequest)WebRequest.Create(this.RemoteUrl);

            // Set some options
            requestToServer.Method = this.Context.Request.HttpMethod;
            requestToServer.UserAgent = this.Context.Request.UserAgent;
            requestToServer.KeepAlive = requestToServer.KeepAlive;
            requestToServer.Proxy = null;

            requestToServer.CookieContainer = new CookieContainer();
            Uri change = new Uri(this.RemoteUrl);
            requestToServer.CookieContainer.Add(change, this.Context.Request.Cookies);
            requestToServer.Headers.Add("x-forward-for", this.Context.Request.RemoteEndPoint.Address.ToString());
            requestToServer.Headers.Add("proxy-version", "1.0");


            // For POST, write the post data extracted from the incoming request
            if (requestToServer.Method == "POST")
            { 
                const int bufferSize = 1024;
                Stream clientStream = this.Context.Request.InputStream;
                byte[] clientPostData = new byte[bufferSize];
                requestToServer.ContentType = this.Context.Request.ContentType;
                int read = clientStream.Read(clientPostData, 0, bufferSize);
                StringBuilder data = new StringBuilder();
                Stream stream = requestToServer.GetRequestStream();
                //read data untile the end of the stream and write it to the rquest sended
                while (read > 0)
                {
                    
                    string tempString = Encoding.UTF8.GetString(clientPostData, 0, clientPostData.Length);
                    tempString=HttpUtility.UrlDecode(tempString);
                    tempString = System.Uri.UnescapeDataString(tempString);
                    /*****************************************************************/

                    string pattern = @"\bbgu\b";
                    string replace = "BenGurion";
                    string res = Regex.Replace(tempString, pattern, replace, RegexOptions.IgnoreCase);
                    Regex emailRegex = new Regex(@"\bbgu\b", RegexOptions.IgnoreCase);
                    //find items that matches with our pattern
                    MatchCollection emailMatches = emailRegex.Matches(tempString.ToString());
                    foreach (Match emailMatch in emailMatches)
                    {
                        Log.Info("Word transformed: "+emailMatch.Value+" -> BenGurion");
                    }

                    pattern = @"\bise\b";
                    replace = "InformationSystemEngineering";

                    emailRegex = new Regex(@"\bise\b", RegexOptions.IgnoreCase);
                    //find items that matches with our pattern
                    emailMatches = emailRegex.Matches(res.ToString());
                    foreach (Match emailMatch in emailMatches)
                    {
                        Log.Info("Word transformed: " + emailMatch.Value + " -> InformationSystemEngineering");
                    }


                    res = Regex.Replace(res, pattern, replace, RegexOptions.IgnoreCase);
                   



                    pattern = @"\bse\b";
                    replace = "SoftwareEngineering";


                    emailRegex = new Regex(@"\bse\b", RegexOptions.IgnoreCase);
                    //find items that matches with our pattern
                    emailMatches = emailRegex.Matches(res.ToString());
                    foreach (Match emailMatch in emailMatches)
                    {
                        Log.Info("Word transformed: " + emailMatch.Value + " -> SoftwareEngineering");
                    }
                    res = Regex.Replace(res, pattern, replace, RegexOptions.IgnoreCase);
                     
                    byte [] buffer =new byte[res.Length*(sizeof (char))];
                    buffer = Encoding.UTF8.GetBytes(res);
                    data.Append(res);
                    /*****************************************************************/
                    stream.Write(buffer, 0, buffer.Length);
                    read = clientStream.Read(clientPostData, 0, bufferSize);
                   
                }
                stream.Close();


            
                //TODO: check for mails in the request (use data)
                string data1 = System.Uri.UnescapeDataString(data.ToString());
                //string data1 = data.ToString();
                if (Regex.IsMatch(data1, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"))
                {
                    Regex emailRegex = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
                    //find items that matches with our pattern
                    MatchCollection emailMatches = emailRegex.Matches(data1.ToString());
                    foreach (Match emailMatch in emailMatches)
                    {
                        Log.Info("Email blocked: " + emailMatch.Value);
                    }
                    return null;
                }

            }

            //check for mails in the request headers

            for (int i = 0; i < this.Context.Request.Headers.Count; i++)
            { 
                if (Regex.IsMatch(System.Uri.UnescapeDataString(this.Context.Request.Headers[i]), @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"))
                {
                    Regex emailRegex = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",RegexOptions.IgnoreCase);
                    //find items that matches with our pattern
                    MatchCollection emailMatches = emailRegex.Matches(System.Uri.UnescapeDataString(this.Context.Request.Headers[i]));
                    foreach (Match emailMatch in emailMatches)
                    {
                        Log.Info("Email blocked: " + emailMatch.Value);
                    }
                    return null;
                }

            }

            for (int j = 0; j < requestToServer.Headers.Count; j++)
            {
                string first = requestToServer.Headers.GetKey(j);
                string input = HttpUtility.UrlDecode(requestToServer.Headers[j].ToString());
                input = System.Uri.UnescapeDataString(input);
                string pattern = @"\bbgu\b";
                string replace = "BenGurion";

                Regex emailRegex = new Regex(@"\bbgu\b", RegexOptions.IgnoreCase);
                //find items that matches with our pattern
                MatchCollection emailMatches = emailRegex.Matches(input.ToString());
                foreach (Match emailMatch in emailMatches)
                {
                    Log.Info("Word transformed: " + emailMatch.Value + " -> BenGurion");
                }

                string res = Regex.Replace(input, pattern, replace, RegexOptions.IgnoreCase);
                

                pattern = @"\bise\b";
                replace = "InformationSystemEngineering";

                emailRegex = new Regex(@"\bise\b", RegexOptions.IgnoreCase);
                //find items that matches with our pattern
                emailMatches = emailRegex.Matches(res.ToString());
                foreach (Match emailMatch in emailMatches)
                {
                    Log.Info("Word transformed: " + emailMatch.Value + " -> InformationSystemEngineering");
                }

                res = Regex.Replace(res, pattern, replace, RegexOptions.IgnoreCase);

                pattern = @"\bse\b";
                replace = "SoftwareEngineering";
                emailRegex = new Regex(@"\bse\b", RegexOptions.IgnoreCase);
                //find items that matches with our pattern
                emailMatches = emailRegex.Matches(res.ToString());
                foreach (Match emailMatch in emailMatches)
                {
                    Log.Info("Word transformed: " + emailMatch.Value + " -> SoftwareEngineering");
                }
                res = Regex.Replace(res, pattern, replace, RegexOptions.IgnoreCase);

                if (!(res.Equals(requestToServer.Headers[j].ToString())))
                {
                    requestToServer.Headers.Remove(first);
                    requestToServer.Headers.Add(first, res);

                }

            

            }

            return requestToServer;
        }


       
        /// <summary>
        /// Send the request to the remote server and return the response
        /// </summary>
        /// <param name="request">Request to send to the server </param>
        /// <returns>Response received from the remote server
        ///           or null if page not found </returns>
        private HttpWebResponse GetResponse(HttpWebRequest request)
        {
            HttpWebResponse response;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException)
            {
                // Send 404 to client 
                this.Context.Response.StatusCode = 404;
                this.Context.Response.StatusDescription = "Page Not Found";
                //this._context.Response.("Page not found");
                this.Context.Response.Close();
                Console.WriteLine("404: Page Not Found");
                return null;
            }

            return response;
        }


        /// <summary>
        /// Return the response in bytes array format
        /// </summary>
        /// <param name="response">Response received
        ///             from the remote server </param>
        /// <returns>Response in bytes </returns>
        private byte[] GetResponseStreamBytes(HttpWebResponse response)
        {
            const int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            MemoryStream memoryStream = new MemoryStream();
            //get the response
            Stream responseStream = response.GetResponseStream();
            //read all the data from the response and save it in a memory stream
            if (responseStream != null)
            {
                int remoteResponseCount = responseStream.Read(buffer, 0, bufferSize);

                while (remoteResponseCount > 0)
                {
                    memoryStream.Write(buffer, 0, remoteResponseCount);
                    remoteResponseCount = responseStream.Read(buffer, 0, bufferSize);
                }
            }
            else
            {
                throw new Exception("Response is null");
            }

            //check for mails in the data and headers of the response
            StringBuilder sb = new StringBuilder();
            byte[] responseData = memoryStream.ToArray();
            string tempString = Encoding.UTF8.GetString(responseData, 0, responseData.Length);
            sb.Append(tempString);

            // TODO: email checking

            // close the open streams
            memoryStream.Close();
            responseStream.Close();
            memoryStream.Dispose();
            responseStream.Dispose();

            return responseData;
        }

        /// <summary> 
        ///  send the response back to the client
        /// </summary>
        /// <param name="response"> the response to send back </param>
        /// <param name="responseData"> the response data to send back </param>
        private void SendResponse(HttpWebResponse response, byte[] responseData)
        {
            // Send the response to client
            this.Context.Response.ContentEncoding = Encoding.UTF8;
            this.Context.Response.ContentType = response.ContentType;
            try
            {
                this.Context.Response.OutputStream.Write(responseData, 0, responseData.Length);
                this.Context.Response.Close();
            }
            catch (Exception )
            {
                Console.WriteLine("could not finish proxy request");
            }
        }

    }
}
